package com.neural.infrastructure;

import java.util.ArrayList;
import java.util.List;

import javax.management.RuntimeErrorException;

import com.neural.activation.Activation;
import com.neural.activation.ActivationManager;
import com.neural.descriptor.LayerDescriptor;
import com.neural.descriptor.NeuronDescriptor;
import com.neural.descriptor.Point;
import com.neural.descriptor.SizeDescriptor;
import com.neural.randomizer.Randomizer;

public class NormalLayer implements ILayer {

	private final List<Neuron> neurons;
	private final Randomizer randomizer;
	private final Activation activationMethod;
	private final SizeDescriptor sizeDescriptor;

	public NormalLayer(LayerDescriptor layerDescriptor, ILayer previousLayer) {
		this.sizeDescriptor = layerDescriptor.getSizeDescriptor();
		this.randomizer = new Randomizer(layerDescriptor.getRange(), 1000);
		this.activationMethod = ActivationManager.getActivationFromName(layerDescriptor.getActivationMethod());
		this.neurons = createNeurons(layerDescriptor.getNeuronsDescriptor(), previousLayer, randomizer);
	}

	private List<Neuron> createNeurons(List<NeuronDescriptor> neuronDescriptors, ILayer previousLayer, Randomizer randomizer) {
		List<Neuron> neurons = new ArrayList<Neuron>(neuronDescriptors.size());
		int neuronNum = 0;
		for (NeuronDescriptor neuronDescriptor : neuronDescriptors) {
			Neuron neuron = new Neuron(neuronDescriptor, previousLayer, randomizer, new Point(neuronNum, sizeDescriptor));
			neurons.add(neuron);
			neuronNum++;
		}
		return neurons;
	}

	public List<Neuron> getNeurons() {
		return neurons;
	}

	public SizeDescriptor getSizeDescriptor() {
		return sizeDescriptor;
	}

	public void setNeuronValues(List<List<Double>> inputs) {
		if (inputs.size() * inputs.get(0).size() != neurons.size()) {
			throw new RuntimeErrorException(new Error());
		}
		for (int i = 0; i < inputs.size(); i++) {
			for (int j = 0; j < inputs.get(i).size(); j++) {
				neurons.get(i * inputs.get(i).size() + j).setValue(inputs.get(i).get(j));
			}
		}
	}

	@Override
	public void compute(Double learningSpeed, Double conscience, Double neighbourhood) {
		for (Neuron neuron : neurons) {
			neuron.compute(activationMethod, learningSpeed, neighbourhood);
		}
	}

	public List<Double> getValues() {

		List<Double> values = new ArrayList<Double>(neurons.size());
		for (int i = 0; i < neurons.size(); i++) {
			values.add(neurons.get(i).getValue());
		}
		return values;
	}

}
